Python socket 粘包

1 TCP的三次握手四次挥手

1.1 三次握手

  • 1 客户端向服务端发起SYN请求,请求建立连接,

  • 2 服务端同意建立连接,回应ACK,同时服务端向客户端发起SYN请求

  • 3 客户端回应ACK

1.2 四次挥手

挥手是任意的,客户端和服务端都可以首先断开连接

下面是以客户端发起的挥手

  • 1 客户端发送完数据后发起FIN 请求,之后直接断开,
  • 2 服务端返回一个ACK
  • 3此时服务端还没有接收完数据,所以没有断开连接,接收完后,服务端想要断开,发送FIN
  • 4 客户端返回ACK,结束

现实中的是TIME WAIT大量存在于服务端,是服务端主动断开连接,它在等待客户端发送ACK

总结

1 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的.

2 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态

这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

3 TIMEWAIT的作用?

主动关闭的Socket端会进入TIME_WAIT状态,并且持续2MSL时间长度,MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒,因而,TIME_WAIT状态一般维持在1-4分钟。

[TCP资料](http://blog.csdn.net/u011726984/article/details/50781212

2 粘包现象

粘包存在两个方面,一个是服务端,一个是客户端

2.1 基于TCP制作远程执行命令操作(win服务端)

下面是服务端放在本地win平台,用127.0.0.1自己测试的代码

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# coding:utf-8
# 基于TCP制作的远程执行命令的操作
# version;版本1
# 问题:有粘包现象
import socket
import subprocess
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None) # 买手机
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8888)) # 绑定手机卡
phone.listen(5) # 开机 相当于挂起的电话连接
print("starting...")
while True: # 通信循环
conn, addr = phone.accept() # 等待电话连接
while True: #
try: # 应对Windows系统 从缓存中读取空的情况
data = conn.recv(1024) # 收消息 从自己的缓存中找数据是最大的值
if not data:
continue # 应对Linux系统从缓存中读取空的情况
res = subprocess.Popen(
data.decode("utf-8"), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # 把读取的信息存放在管道中
'''
# 这是判断错误的方式
err = res.stderr.read() # 首先判断错误信息
if not err: # 这是读取到的正常信息
res_cmd = res.stdout.read()
else:
res_cmd = err # 这是读取道的错误信息
conn.send(res_cmd) # 最后把接收到的数据发送到客户端
'''
# 这是直接发送的数据
res_err = res.stderr.read()
res_out = res.stdout.read()
conn.send(res_err)
conn.send(res_out)
except Exception:
break
conn.close() # 断开连接
phone.close() # 关闭服务器连接

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 8888)) # 直接进行连接
while True:
cmd = input(">>:").strip() # 输入去除空格 换行
if not cmd:
continue # 没有输入内容 继续输入
# phone.send(cmd.encode("utf-8")) #
phone.send(bytes(cmd.encode("utf-8"))) # 默认发送的是字节bytes
data = phone.recv(1024) # 从客户端自己的缓存中寻找
print(data.decode('gbk')) # win 平台的默认编码是gbk 在Linux中用uft-8
phone.close() # 关闭客户端

实验的时候首先开启服务端,然后开启客户端

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>:cd
D:\Python\...\....
>>:listen
'listen' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
>>:ls
'ls' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
>>:tree
卷 我的小D 的文件夹 PATH 列表
卷序列号为 0005-B7F0
D:.
没有子文件夹
>>:hello
'hello' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
>>:

2.1 基于TCP制作远程执行命令操作(Linux服务端)

在Linux上需要修改IP地址位服务端的地址,客户端的解码方式修改成utf-8

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# coding:utf-8
# 基于TCP制作的远程执行命令的操作
# version;版本1
# 问题:有粘包现象 具体看执行的结果
import socket
import subprocess
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None) # 买手机
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('192.168.16.200', 8800)) # 修改成Linux自己的IP地址
phone.listen(5) # 开机 相当于挂起的电话连接
print("starting...")
while True: # 通信循环
conn, addr = phone.accept() # 等待电话连接
while True: #
try: # 应对Windows系统 从缓存中读取空的情况
data = conn.recv(1024) # 收消息 从自己的缓存中找数据是最大的值
if not data:
continue # 应对Linux系统从缓存中读取空的情况
res = subprocess.Popen(
data.decode("utf-8"), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # 把读取的信息存放在管道中
'''
# 这是判断错误的方式
err = res.stderr.read() # 首先判断错误信息
if not err: # 这是读取到的正常信息
res_cmd = res.stdout.read()
else:
res_cmd = err # 这是读取道的错误信息
conn.send(res_cmd) # 最后把接收到的数据发送到客户端
'''
# 这是直接发送的数据
res_err = res.stderr.read()
res_out = res.stdout.read()
conn.send(res_err)
conn.send(res_out)
except Exception:
break
conn.close() # 断开连接
phone.close() # 关闭服务器连接

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# coding:utf-8
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# phone.connect(("127.0.0.1", 8888)) # 直接进行连接
phone.connect(("192.168.16.200", 8800)) # Linux测试
while True:
cmd = input(">>:").strip() # 输入去除空格 换行
if not cmd:
continue # 没有输入内容 继续输入
# phone.send(cmd.encode("utf-8")) #
phone.send(bytes(cmd.encode("utf-8"))) # 默认发送的是字节bytes
data = phone.recv(1024) # 从客户端自己的缓存中寻找
# print(data.decode('gbk')) # win 平台的默认编码是gbk 在Linux中用uft-8
print(data.decode('utf-8')) # win 平台的默认编码是gbk 在Linux中用uft-8
phone.close() # 关闭客户端

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
>>:pwd
/
>>:cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-bus-proxy:x:999:998:systemd Bus Proxy:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:998:997:User for polkitd:/:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
hzx:x:1000:1000::/home/hzx:/bin/bash
help:x:1001:1001::/home/hel
>>:ls # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!看下面的就是粘包
p:/bin/bash
egon:x:1002:1002::/home/egon:/bin/bash
tom:x:1004:1004::/home/tom:/bin/bash
jake:x:1005:1006::/home/jake:/bin/bash
rose:x:1006:1007::/home/rose:/bin/bash
nginx:x:997:995:Nginx web server:/var/lib/nginx:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
>>:ls # 现在才是正常执行的结果
bin
boot
dev
etc
FtpServer
hello.py
home
lib
lib64
media
mnt
opt
proc
Python-3.6.0
Python-3.6.0.tgz
root
run
sbin
sdb1
sdb2
sdb3
server.py
share
srv
sys
tail_new.py
tail.py
tartmp
test
test3
tmp
tmux
tmux-2.3
tmux-2.3.tar.gz
usr
var
>>:

2.2 粘包产生的原因:

客户端都是从自己的缓存中找,send后就是由操作系统取发送,不知道数据有多长

上面出现了粘包的问题,粘包产生的主要原因是TCP协议是面向流(socket.SOCK_STREAM)),意思就是说发送端可以一个字节一个字节的发送, 软件的速度是大于网络的速度(网络有延迟),TCP把数据量小,时间间隔小的进行封包,一块发送,这就无法区分包的边界

接收端从自己的缓存中是有多少取多少,发送过来的是字节是没有边界的,所以收的时候是通过修改recv(1024)中的参数来增加一次性接收的值,但是这个如果是一个大文件的时候是有问题的,会全部去取到内存中,内存会爆。

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

3 解决粘包

粘包是不知道数据长度,就是没有关于数据的描述信息。1

类似以太网协议,在数据的前面加上固定长度的报头,报头中包含字节流的长度,然后一次send到对端。

对端在接收时,先从自己的缓存中取出定长的报头,然后再获取真实的数据

3.1 struck模块

struck可以把一个类型,如数字转换成bytes

1
2
3
4
5
struct.pact('i',1234567890) # 把数字转换成4个字节
'''
结果:
b'\xd2\x02\x96I'
'''

struc

1
2
3
4
5
6
7
8
9
10
>>> import struct
>>> struct.pack('i',1231)
b'\xcf\x04\x00\x00'
>>> res = struct.pack('i',1231)
>>> res2 = struct.unpack('i',res)
>>> print(res2)
(1231,) # 返回的是一个元组的值
>>> res2 = struct.unpack('i',res)[0] # 取出元组的第一个值
>>> print(res2)
1231

准换数字的范围是: -2147483648 <= number <= 2147483647

3.2制作报头

模拟以太网协议的报头

**固定长度

描述信息**

3.2.1 win版本

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# coding:utf-8
# 基于TCP制作的远程执行命令的操作
# version;版本2
# 解决粘包的问题
import socket
import subprocess
import struct # 把数 据转换成固定的长度
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None) # 买手机
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8800)) # 绑定手机卡
phone.listen(5) # 开机 相当于挂起的电话连接
print("starting...")
while True: # 通信循环
conn, addr = phone.accept() # 等待电话连接
while True: #
try: # 应对Windows系统 从缓存中读取空的情况
data = conn.recv(1024) # 收消息 从自己的缓存中找数据是最大的值
if not data:
continue # 应对Linux系统从缓存中读取空的情况
res = subprocess.Popen(
data.decode("utf-8"), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # 把读取的信息存放在管道中
'''
# 这是判断错误输出的方式
err = res.stderr.read() # 首先判断错误信息
if not err: # 这是读取到的正常信息
res_cmd = res.stdout.read()
else:
res_cmd = err # 这是读取道的错误信息
conn.send(res_cmd) # 最后把接收到的数据发送到客户端
'''
# 这是直接发送的数据
res_err = res.stderr.read()
res_out = res.stdout.read()
# print(type(res_err)) # 通过判判断 从管道中取的是bytes数据
data_size = len(res_err) + len(res_out) # 拼接数据的长度
'''
data_size = str(len(res_err) + len(res_out)) # 统计数据的长度
这种情况仍然是不行的 传输的还是没有分界
'''
# 制作报头.
conn.send(struct.pack('i', data_size)) # 把报头的数据发送 自己定义的数据格式
conn.send(res_err)
conn.send(res_out)
except Exception:
break
conn.close() # 断开连接
phone.close() # 关闭服务器连接

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# coding:utf-8
# 远程执行命令
# 解决粘包的问题
#
import socket
import struct # 客户需要解包
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# phone.connect(("127.0.0.1", 8888)) # 直接进行连接
phone.connect(("127.0.0.1", 8800)) # Linux测试
while True:
cmd = input(">>:").strip() # 输入去除空格 换行
if not cmd:
continue # 没有输入内容 继续输入
# phone.send(cmd.encode("utf-8")) #
phone.send(bytes(cmd.encode("utf-8"))) # 默认发送的是字节bytes
# 首先接收报头 自己固定的4个字节
gram_head = phone.recv(4)
# 取出数据长度
data_size = struct.unpack('i', gram_head)[0] # 现在是标注的数据大小 是元组的第一个值
# 接收数据 循环接收 直到接收完数据
recv_size = 0 # 设置接收到的数据初始值为0
recv_data = b'' # 用于拼接接收到的数据
while recv_size < data_size: # 当接收到的数据小于真实数据的时候 循环接收
data = phone.recv(1024) # 从客户端自己的缓存中寻找
recv_size += len(data) # 把接收到的数据的长度重新赋值给recv_size data 是真实的数据
recv_data += data # 把接收到的data循环拼接到recv_data,接收完后recv_data就是最终的数据
print(recv_data.decode('gbk')) # win 平台的默认编码是gbk 在Linux中用uft-8
# print(data.decode('utf-8')) # win 平台的默认编码是gbk 在Linux中用uft-8
phone.close() # 关闭客户端

3.2.2 Linux 版本

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# coding:utf-8
# 基于TCP制作的远程执行命令的操作
# version;版本2
# 解决粘包的问题
import socket
import subprocess
import struct # 把数 据转换成固定的长度
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None) # 买手机
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 就是它,在bind前加
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('192.168.16.200', 8880)) # 绑定手机卡
phone.listen(5) # 开机 相当于挂起的电话连接
print("starting...")
while True: # 通信循环
conn, addr = phone.accept() # 等待电话连接
while True: #
try: # 应对Windows系统 从缓存中读取空的情况
data = conn.recv(1024) # 收消息 从自己的缓存中找数据是最大的值
if not data:
continue # 应对Linux系统从缓存中读取空的情况
res = subprocess.Popen(
data.decode("utf-8"), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # 把读取的信息存放在管道中
'''
# 这是判断错误输出的方式
err = res.stderr.read() # 首先判断错误信息
if not err: # 这是读取到的正常信息
res_cmd = res.stdout.read()
else:
res_cmd = err # 这是读取道的错误信息
conn.send(res_cmd) # 最后把接收到的数据发送到客户端
'''
# 这是直接发送的数据
res_err = res.stderr.read()
res_out = res.stdout.read()
# print(type(res_err)) # 通过判判断 从管道中取的是bytes数据
data_size = len(res_err) + len(res_out) # 拼接数据的长度
'''
data_size = str(len(res_err) + len(res_out)) # 统计数据的长度
这种情况仍然是不行的 传输的还是没有分界
'''
# 制作报头.
conn.send(struct.pack('i', data_size)) # 把报头的数据发送 自己定义的数据格式
conn.send(res_err)
conn.send(res_out)
except Exception:
break
conn.close() # 断开连接
phone.close() # 关闭服务器连接

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# coding:utf-8
# 远程执行命令
# 解决粘包的问题
#
import socket
import struct # 客户需要解包
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# phone.connect(("127.0.0.1", 8888)) # 直接进行连接
phone.connect(("192.168.16.200", 8880)) # Linux测试
while True:
cmd = input(">>:").strip() # 输入去除空格 换行
if not cmd:
continue # 没有输入内容 继续输入
# phone.send(cmd.encode("utf-8")) #
phone.send(bytes(cmd.encode("utf-8"))) # 默认发送的是字节bytes
# 首先接收报头 自己固定的4个字节
gram_head = phone.recv(4)
# 取出数据长度
data_size = struct.unpack('i', gram_head)[0] # 现在是标注的数据大小 是元组的第一个值
# 接收数据 循环接收 直到接收完数据
recv_size = 0 # 设置接收到的数据初始值为0
recv_data = b'' # 用于拼接接收到的数据
while recv_size < data_size: # 当接收到的数据小于真实数据的时候 循环接收
data = phone.recv(1024) # 从客户端自己的缓存中寻找
recv_size += len(data) # 把接收到的数据的长度重新赋值给recv_size data 是真实的数据
recv_data += data # 把接收到的data循环拼接到recv_data,接收完后recv_data就是最终的数据
# print(recv_data.decode('gbk')) # win 平台的默认编码是gbk 在Linux中用uft-8
print(data.decode('utf-8')) # win 平台的默认编码是gbk 在Linux中用uft-8
phone.close() # 关闭客户端

3.3 上面的问题

上面虽然解决了粘包的问题,但是在在后期传输大的文件的时候,报头的数据就会很大,也就是全部读入内存,会让内存爆。

#### 3.3.1 通过序列化解决问题

可以把报头制作成字典,字典中包含将要发送的数据的真实信息,然后把字典json序列化,然后用struct把序列化后数据打包成4个字节。

发送过程

1 先发送报头长度

2 编码报头内容然后再发送

3 最后发送真实内容

接收过程

1 接收报头的长度,用struct.unpack获得

2 取固定长度的报头(4个字节),然后解码,反序列化

3 从反序列化的字典中的寻找数据的内容,然后获取数据的真实信息

解决粘包的最终版Linux服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#
import socket
import subprocess
import json
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# phone.bind(('127.0.0.1', 8120))
phone.bind(('192.168.16.200', 8220))
phone.listen(5)
print("sever is starting...")
while True: # 通信循环
conn, addr = phone.accept()
print(conn)
print(addr)
while True:
try:
data = conn.recv(1024) # conn.recv
if not data:
continue
res = subprocess.Popen(data.decode("utf-8"),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
res_err = res.stderr.read()
res_out = res.stdout.read()
# 数据长度
data_size = len(res_err) + len(res_out)
# 制作报头
head_dic = {"data_size": data_size} # 把报头制作成字典格式
head_json = json.dumps(head_dic) # json 序列化
head_bytes = head_json.encode("utf-8") # 要发送需要转换成字节数据
# 1 发送报头的长度
head_len = len(head_bytes) # 这是报头的长度
conn.send(struct.pack("i", head_len))
# 2 发送报头
conn.send(head_bytes)
# 3 发送真实的数据
conn.send(res_err)
conn.send(res_out)
except Exception:
break
conn.close()
phone.close()

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#
import socket
import json
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# phone.connect(("192.168.16.200", 8120))
phone.connect(("192.168.43.20", 8220))
while True:
cmd = input(">>:").strip()
if not cmd:
continue
# phone.send(cmd.encode("utf-8"))
phone.send(bytes(cmd,encoding="utf-8"))
# 1 接收报头长度
head_struct = phone.recv(4) # 先接收报头
head_len = struct.unpack("i", head_struct)[0]
# 2 接收报头
head_bytes = phone.recv(head_len)
head_json = head_bytes.decode("utf-8")
head_dic = json.loads(head_json)
print(head_dic)
data_size = head_dic["data_size"]
# 接收数据
recv_size = 0
recv_data = b''
while recv_size < data_size:
data = phone.recv(1024)
recv_size += len(data)
recv_data += data
print(recv_data.decode("utf-8"))
phone.close()

Linux注意上传文件目录的权限777

4 UDP协议

© 2018 Peter's Blog Center All Rights Reserved.
Theme by hiero